<script setup lang="ts">
import { onMounted, ref, computed } from "vue";
import Paramater from "./components/Paramater.vue";
import { Column } from "element-plus";
import { useDark } from "@vueuse/core";
import dayjs from "dayjs";
import { ElMessage } from "element-plus";

// const ipcHandle = () => window.electron.ipcRenderer.send('ping')
useDark();
interface Tree {
  label: string;
  sql: string;
  children?: Tree[];
  description?: string;
}

const source = ref("");
const result = ref<object[]>([]);
const selected = ref<object | null>(null);
const showSql = ref(false);

const description = ref("");
interface Param {
  name: string;
  ctrl: string;
  value: string | number | Date | null;
}
const params = ref<Param[]>([]);

const handleNodeClick = (item: Tree) => {
  source.value = item["sql"] || "";
  description.value = item["description"] || "";
  params.value = [];
  selected.value = null;
  result.value = [];

  if (sql.value) {
    // 解析 sql, 查找 ${...} 中的文本
    const reg = /\$\{([^}]+)\}/g;
    const matches = source.value.match(reg);
    if (matches) {
      for (const match of matches) {
        const ctrl = match.slice(2, -1);
        const sp = ctrl.split(":");
        const name = sp[0];
        params.value.push({ name, ctrl, value: null });
      }
    }
  }
};

const data = ref<Tree[]>([]);

const defaultProps = {
  children: "children",
  label: "label",
};

const refresh = async () => {
  try {
    config.value = await window.api.getConfig();

    const files = await window.api.getFiles();

    data.value = [];
    if (files)
      for (const file of files) {
        // 移除目录和后缀
        let name = file.replace(/\.[^/.]+$/, "");
        name = name.replace(/.*[\/\\]/, "");

        const sqlFile = window.api.parseSqlFile(file);
        console.log("result: ", sqlFile);

        const node: Tree = { label: name, children: [], description: "", sql: "" };

        for (const [key, sql] of Object.entries<string>(sqlFile)) {
          if (key) {
            const split = key.split(":");
            const label = split[0];
            const description = split.length > 1 ? split[1] : "";
            if (!node.children) {
              node.children = [];
            }
            node.children.push({ label, sql, description });
          } else {
            node["sql"] = sql;
          }
        }
        data.value.push(node);
      }
  } catch (e) {
    return;
  }
};

onMounted(async () => {
  await refresh();
  if (!config.value.username) {
    if (!config.value.databaseType) config.value.databaseType = "mysql";
    showSetting.value = true;
  }
});

const sql = computed(() => {
  let sqlText = source.value || "";
  for (const param of params.value) {
    sqlText = sqlText.replace(
      "${" + param.ctrl + "}",
      param.value ? param.value.toString() : '<span class="red">' + param.name + "</span>"
    );
  }
  return sqlText;
});

const onQuery = async () => {
  try {
    if (sql.value.indexOf("<span") > -1) {
      ElMessage.error("请填写全部参数");
      return;
    }
    error.value = "";
    const v = await window.api.executeQuery(sql.value);
    console.log(v[0]);
    result.value = v;
    if (v.length === 1) selected.value = v[0];
  } catch (e: any) {
    error.value = e.message;
  }
};

const columns = computed(() => {
  const cols: Column[] = [];
  for (const key of Object.keys(result.value[0])) {
    cols.push({
      key,
      dataKey: key,
      title: key,
      width: 150,
    });
  }
  return cols;
});

const rowEventHandlers = {
  onDblclick: (e) => {
    selected.value = e.rowData;
  },
};

const fmt = (v: any) => {
  // 判断是否 date
  if (v instanceof Date) {
    return dayjs(v).format("YYYY-MM-DD HH:mm:ss");
  }
  return v;
};

const showSetting = ref(false);
const config = ref({
  databaseType: "mysql",
  host: "",
  port: "",
  username: "",
  password: "",
  database: "",
  path: "",
});

const handleClose = (done: () => void) => {
  done();
};

const onConfirm = async () => {
  showSetting.value = false;
  await window.api.setConfig(JSON.stringify(config.value));

  refresh();
};

const onSelectPath = async () => {
  const path = await window.api.selectPath("" + config.value.path);
  if (path) config.value.path = path[0];
};

const error = ref("");
</script>

<template>
  <el-container style="width: 100%; height: 100%">
    <el-dialog
      v-model="showSetting"
      title="设置"
      width="500"
      :close-on-click-modal="false"
      :before-close="handleClose"
    >
      <div class="flex-column">
        <div class="flex">
          <div class="label">数据库类型：</div>
          <el-radio-group v-model="config.databaseType">
            <el-radio value="mysql">MySql</el-radio>
          </el-radio-group>
        </div>
        <div class="flex">
          <div class="label">数据库地址：</div>
          <el-input v-model="config.host" placeholder="请输入数据库地址" />
        </div>
        <div class="flex">
          <div class="label">端口：</div>
          <el-input v-model="config.port" placeholder="请输入端口" />
        </div>
        <div class="flex">
          <div class="label">用户名：</div>
          <el-input v-model="config.username" placeholder="请输入用户名" />
        </div>
        <div class="flex">
          <div class="label">密码：</div>
          <el-input
            v-model="config.password"
            placeholder="请输入密码"
            type="password"
            show-password
          />
        </div>
        <div class="flex">
          <div class="label">数据库名称：</div>
          <el-input v-model="config.database" placeholder="（可选）请输入数据库名称" />
        </div>
        <div class="flex">
          <div class="label">脚本存放路径：</div>
          <el-input v-model="config.path" placeholder="请输入脚本存放路径">
            <template #suffix>
              <el-icon style="cursor: pointer" @click="onSelectPath"
                ><Folder /></el-icon></template
          ></el-input>
        </div>
      </div>
      <template #footer>
        <div class="dialog-footer">
          <el-button @click="showSetting = false">取消</el-button>
          <el-button type="primary" @click="onConfirm">确定</el-button>
        </div>
      </template>
    </el-dialog>
    <el-aside class="aside">
      <div class="setting">
        <el-button type="default" size="small" icon="Setting" @click="showSetting = true"
          >设置</el-button
        >
      </div>
      <el-tree
        class="tree"
        :data="data"
        :props="defaultProps"
        :highlight-current="true"
        @node-click="handleNodeClick"
      />
    </el-aside>
    <el-container>
      <el-main>
        <div class="setting" v-if="sql">
          <el-button
            type="default"
            v-if="showSql"
            size="small"
            @click="showSql = false"
            icon="ArrowUp"
            style="float: right"
          >
            隐藏 SQL</el-button
          >
          <el-button
            type="default"
            v-else
            size="small"
            @click="showSql = true"
            icon="ArrowDown"
            style="float: right"
          >
            查看 SQL</el-button
          >
        </div>
        <div class="box" v-if="description">{{ description }}</div>
        <div
          v-if="showSql && sql"
          class="box"
          style="position: relative; word-wrap: break-word"
          v-html="sql"
        ></div>
        <div class="paramaters">
          <paramater
            v-for="(param, idx) in params"
            :key="idx"
            v-model="param.value"
            :param="param.ctrl"
          />
        </div>
        <div class="search" v-if="sql">
          <div style="text-align: center; margin: 10px; position: relative">
            <el-button type="primary" @click="onQuery" style="width: 100px"
              >查询</el-button
            >
          </div>
        </div>
        <el-alert
          :title="error"
          type="error"
          effect="dark"
          v-if="error"
          :closable="false"
        />
        <div v-if="result.length > 0" style="width: 100%; height: 80%">
          <div class="object" v-if="selected">
            <div class="back">
              <el-button
                type="default"
                v-if="result.length > 1"
                @click="selected = null"
                small
                ><el-icon color="#ccc"><DArrowLeft /></el-icon>返回</el-button
              >
            </div>
            <div class="col" v-for="(obj, idx) in Object.entries(selected)" :key="idx">
              <div class="key">{{ obj[0] }}</div>
              <div class="value">{{ fmt(obj[1]) }}</div>
            </div>
          </div>
          <el-auto-resizer v-else>
            <template #default="{ height, width }">
              <el-table-v2
                :columns="columns"
                :width="width"
                :height="height"
                :data="result"
                row-class="row-class"
                class="table"
                :row-event-handlers="rowEventHandlers"
              >
              </el-table-v2>
            </template>
          </el-auto-resizer>
        </div>
      </el-main>
    </el-container>
  </el-container>
</template>

<style scoped lang="scss">
.title {
  font-family: 微软雅黑;
  font-size: 24px;
  font-weight: bold;
  color: #4a71d4;
  margin: 10px 0;
  text-align: center;
  width: 100%;
  padding: 10px;
  background-color: rgb(21, 20, 18);
  border-radius: 4px;
}

.label {
  text-align: right;
  width: 25%;
  margin-right: 10px;
  flex: none;
}

.search {
  margin: 10px 0;
}

.el-container {
  height: 100%;
}

.el-aside {
  width: 20%;
}

.aside {
  padding: 10px 0 10px 10px;
}

.tree {
  border-radius: 4px;
  background-color: rgb(21, 20, 18);
}

.paramaters {
  display: flex;
  flex-wrap: wrap;
}

.box {
  padding: 5px 10px;
  border: #666 1px solid;
  border-radius: 4px;
  margin: 4px 0;
}

.table {
  background: transparent;
}

.back {
  padding: 10px;
}
.col {
  display: flex;
  flex-direction: row;
  justify-content: space-between;

  .key {
    width: 20%;
    text-align: right;
  }
  .value {
    text-align: left;
    flex: auto;
    margin-left: 10px;
    border-bottom: 1px solid #666;
  }
}

.setting {
  margin: -10px 0 5px;
  height: 28px;
}
</style>
